/******************
 *  init_dirs()
 *  inicializace prace se slozkami
 */
int init_dirs(void) {
  _dir_actual           = mem_alloc(sizeof(ActualDir), KERNEL_SEGMENT);
  _dir_find_file        = mem_alloc(sizeof(DirFindFile), KERNEL_SEGMENT);
  _dir_actual->c_start  = DIR_ROOT;
  _dir_actual->c_actual = DIR_ROOT;
  _dir_actual->pos      = 20;    /* (20-1)*512 = 0x2600 - zde zacina prvni
                                       sektor struktury korenove slozky "/" */
  _dir_actual->string   = mem_alloc(128, KERNEL_SEGMENT);

  strcpy(_dir_actual->string, "/");

  _dir_find_file->last_cluster          = -1;
  _dir_find_file->buffer                = mem_alloc(512, KERNEL_SEGMENT);
  _dir_find_file->buffer_backup_pointer = _dir_find_file->buffer;

  return 0;
}

/**
 *  create_dir()
 *  vytvori slozku <filename> v jiz existujici slozce(!!)
 *  IN: <filename>      - cesta k nove slozce + nova slozka
 *                        (pr. "/etc/dir", kde "/etc" je jiz existujici slozka)
 *  OUT: <result>       - vyssledek operace
 */
int create_dir(char far *filename) {
  int i           = 0;                              /* cyklus */
  int lastCluster = 0;                              /* posledni cluster */
  int freeCluster = 0;                              /* volny cluster */
  int page        = 0;                              /* strankovani slozky */
  int lpos        = 0;                              /* pozice v clusteru */
  int Dot         = 0;                              /* cluster pro slozku "." */
  int DotDot      = 0;                              /* cluster pro slozku ".." */
  int result      = CREATE_DIR_OK;                  /* navratova hodnota */
  char far *realFilename      = mem_alloc(32,
                                  KERNEL_SEGMENT);  /* alokujeme si misto, kde bude nazev souboru velkymi pismeny */
  char far *buffer            = mem_alloc(0x220,
                                  KERNEL_SEGMENT);  /* alokujeme 512 bajtu pro sektor + 32 bajtu pro pripadny novy soubor */
  DirectoryEntry far *sfile   = mem_alloc(sizeof(DirectoryEntry),
                                  KERNEL_SEGMENT);
  FatBackupData  far *fbd     = fat_dirs_begin(&filename);

  /* vyparsujeme slozku a nazev souboru */
  fat_get_filename(filename, realFilename);

  /* zjistime, zda-li soubor/slozka jiz neexistuje */
  while((i = find_file(sfile, i)) != END_OF_DIR) {
    if(!strcmp(sfile->realname, filename)) {   /* existuje? */
      result = CREATE_DIR_FAILED;
      goto create_dir_end;
    }
  }

  /* vratime ukazatel na soubor na 0, a zjistime nejblizsi mozne volne
    misto (i po vymazanych souborech) pro nasi novou slozku */
  i=0;
  while((i = find_file_care(sfile, i, &page, &lpos, true)) != END_OF_DIR);

  if(_dir_actual->c_start == DIR_ROOT) {                                /* pokud zapisujeme do "/" */
    /* zapiseme slozku */
    Dot    = fat_get_free_cluster();                                    /* zjistime nejblizsi volny cluster ve FAT, to bude cluster pro nasi novou slozku */
    DotDot = 0;                                                         /* cluster pro "/" je vzdy 0 */
    floppyread((_dir_actual->pos+page), 1, buffer);                     /* nacteme strukturu */
    fat_entry_assign(buffer+(lpos*32), realFilename, F_DIRECTORY);      /* vytvorime si v pameti novou slozku */
    *((unsigned int far *)(buffer+(lpos*32)+0x1A)) = Dot;               /* dosadime cluster na novou slozku */
    floppywrite((_dir_actual->pos+page), 1, buffer);                    /* zapiseme */
    /* a jeji prvni obsah "." a ".." */
    floppyread((_fat->data_start_sector + (Dot - 2)), 1, buffer);       /* nacteme cluster s nasi novou slozkou */
    fat_entry_assign(buffer, ".          ", F_DIRECTORY);               /* vytvorime si v pameti slozku "." */
    *((unsigned int far *)(buffer+0x1A)) = Dot;
    fat_entry_assign(buffer+32, "..         ", F_DIRECTORY);            /* vytvorime si v pameti slozku ".." */
    *((unsigned int far *)(buffer+32+0x1A)) = DotDot;
    *(buffer+64) = '\0';                                                /* pro uzavreni slozky */
    floppywrite((_fat->data_start_sector + (Dot - 2)), 1, buffer);      /* zapiseme novou slozku */

    fat_write_entry(Dot, FAT_LASTCLUSTER);                              /* zapiseme do FAT (nova slozka nebude mit vice, nez 1 blok :)) */

    fat_save();                                                         /* ulozime si FAT */
  } else {
    if(!lpos) {                                                         /* pokud zapisujeme novy blok */
      lastCluster = fat_get_last_cluster_of_actual_dir();               /* zjistime posledni cluster nasi slozky */
      freeCluster = fat_get_free_cluster();                             /* zjistime nejblizsi volny cluster ve FAT */

      fat_write_entry(lastCluster, freeCluster);                        /* zapiseme pokracovani slozky do FAT */
      fat_write_entry(freeCluster, FAT_LASTCLUSTER);                    /* ukoncime zaznam */

      Dot    = fat_get_free_cluster();                                  /* dosadime cluster pro "." */
      DotDot = _dir_actual->pos;                                        /* a pro ".." */

      floppyread((_fat->data_start_sector + (freeCluster - 2)),
                       1, buffer);                                      /* nacteme strukturu slozky */
      fat_entry_assign(buffer+(lpos*32), realFilename, F_DIRECTORY);    /* vytvorime si v pameti novou slozku */
      *((unsigned int far *)(buffer+0x1A)) = Dot;                       /* dosadime cluster na novou slozku */
      *(buffer+32) = '\0';                                              /* pro uzavreni slozky */
      floppywrite((_fat->data_start_sector + (freeCluster - 2)),
                        1, buffer);                                     /* zapiseme upraveny sektor */
      fat_save();                                                       /* ulozime si FAT */

      /* a jeji prvni obsah "." a ".." */
      floppyread((_fat->data_start_sector + (Dot - 2)), 1, buffer);     /* nacteme cluster s nasi novou slozkou */
      fat_entry_assign(buffer, ".          ", F_DIRECTORY);             /* vytvorime si v pameti slozku "." */
      *((unsigned int far *)(buffer+0x1A)) = Dot;
      fat_entry_assign(buffer+32, "..         ", F_DIRECTORY);          /* vytvorime si v pameti slozku ".." */
      *((unsigned int far *)(buffer+32+0x1A)) = DotDot;
      *(buffer+64) = '\0';                                              /* pro uzavreni slozky */
      floppywrite((_fat->data_start_sector + (Dot - 2)), 1, buffer);    /* zapiseme novou slozku */

      fat_write_entry(Dot, FAT_LASTCLUSTER);                            /* zapiseme do FAT (nova slozka nebude mit vice, nez 1 blok :)) */

      fat_save();                                                       /* ulozime si FAT */
    } else {                                                            /*   !- nezapisujeme novy blok */
      Dot    = fat_get_free_cluster();
      DotDot = _dir_actual->pos;

      lastCluster = fat_get_last_cluster_of_actual_dir();               /* zjistime posledni cluster nasi slozky */
      floppyread((_fat->data_start_sector + (lastCluster - 2)),
                        1, buffer);                                     /* nacteme pozadovany sektor */
      fat_entry_assign(buffer+(lpos*32), realFilename, F_DIRECTORY);    /* vytvorime si v pameti soubor */
      *((unsigned int far *)(buffer+(lpos*32)+0x1A)) = Dot;             /* dosadime cluster na novou slozku */
      *(buffer+(lpos*32)+32) = '\0';                                    /* pro uzavreni slozky */
      floppywrite((_fat->data_start_sector + (lastCluster - 2)),
                        1, buffer);                                     /* zapiseme upraveny sektor */

      /* a jeji prvni obsah "." a ".." */
      floppyread((_fat->data_start_sector + (Dot - 2)), 1, buffer);     /* nacteme cluster s nasi novou slozkou */
      fat_entry_assign(buffer, ".          ", F_DIRECTORY);             /* vytvorime si v pameti slozku "." */
      *((unsigned int far *)(buffer+0x1A)) = Dot;
      fat_entry_assign(buffer+32, "..         ", F_DIRECTORY);          /* vytvorime si v pameti slozku ".." */
      *((unsigned int far *)(buffer+32+0x1A)) = DotDot;
      *(buffer+64) = '\0';                                              /* pro uzavreni slozky */
      floppywrite((_fat->data_start_sector + (Dot - 2)), 1, buffer);    /* zapiseme novou slozku */

      fat_write_entry(Dot, FAT_LASTCLUSTER);                            /* zapiseme do FAT (nova slozka nebude mit vice, nez 1 blok :)) */

      fat_save();                                                       /* ulozime si FAT */
    }
  }

  /* uvolnime buffery */
  create_dir_end:
  fat_dirs_end(fbd);

  mem_free(buffer);
  mem_free(realFilename);
  mem_free(sfile);
  return result;
}

/**
 *  find_file_care()
 *  najde soubor v aktualni slozce na pozici <pos>, nastavi <sfile>
 *  a jine promenne
 *  IN:   <sfile>         - [DirectoryEntry], tato hodnota se asociuje
 *        <pos>           - pozice ve slozce
 *        <*_page>        - je vraceno jako cislo klastru aktualni slozky
 *        <*_pos>         - zde je vracena pozive v klastru
 *        <breakAtDelete> - [bool] rozhoduje, zda-li kdyz narazi na vymazany soubor, ma pokracovat nebo ne
 *  OUT:  [int] pristi pozice
 */
int find_file_care(DirectoryEntry far *sfile, int pos, int far *_page, int far *_pos, bool breakAtDelete) {
  int result = pos;           /* navratova hodnota vychazi z pozice ve slozce */
  int page   = 0;             /* cislo stranky slozky */
  int lpos   = 0;             /* lokalni pozice v clusteru */
  int i      = 0;             /* promenna pro cykly */

  _dir_find_file->buffer = _dir_find_file->buffer_backup_pointer;     /* nastavime pointer pro buffer */
  page  = (pos/16);                                                   /* ((pos*32)/512) zjistime na kolikate strance je pointer na dalsi soubor */
  lpos  = (pos%16);                                                   /* (((pos*32)%512)*32) jeste vypocitame pozici v clusteru */
  if(_dir_actual->c_start != DIR_ROOT) {                              /* |- pokud nejsme v korenovem adresari "/" */
    for(i=0; i < page; i++) {                                         /* prepocitame pozici ve strukture slozky podle pozice */
      fat_get_next_cluster(&(_dir_actual->c_actual));                 /* dalsi cluster */
    }
    if(fat_end(_dir_actual->c_actual)) {                              /* pokud jsme narazili na konec slozky */
      result = END_OF_DIR;                                            /* vratime END_OF_DIR */
      goto _end_of_dir;                                               /* skocime na zaverecnou cast f-ce */
    }
    _dir_actual->pos  = ((_fat->data_start_sector +
                          (_dir_actual->c_actual - 2)));              /* dosadime do pozice ve strukture slozky */
  } else {                                                            /* |- pokud nejsme v korenovem adresari "/" */
    _dir_actual->pos += page;                                         /* dosadime do pozice ve strukture slozky */
  }

  /* pokud opetovne cteme stejny cluster slozky jako naposled,
   * neni nutne ho opet nacitat, pokud cteme jiny cluster,
   * nacteme novou strukturu */
  if(_dir_actual->pos != _dir_find_file->last_cluster || _fat->changed) {
    floppyread((_dir_actual->pos), 1, _dir_find_file->buffer);         /* nacteme do bufferu strukturu slozky */
    _dir_find_file->last_cluster = _dir_actual->pos;
    _fat->changed           = false;
  }

  _dir_find_file->buffer += (lpos*32);                                /* posuneme pointer podle zbytku pozice (pos%16) */

  while(1) {                                                          /* cyklus - hledani souboru */
    if (!*_dir_find_file->buffer || fat_end(_dir_actual->c_actual)) { /* konec slozky? Vratime END_OF_DIR */
      result = END_OF_DIR;
      break;
    }
    if ((*_dir_find_file->buffer == DELETED) ||
          (*(_dir_find_file->buffer+0x0B) == F_VOLUME) ||
            (*(_dir_find_file->buffer+0x0B) == 0x0F)) {               /* |- vymazany soubor? Label? LFN? */

      if((*_dir_find_file->buffer == DELETED) && (breakAtDelete)) {   /* pokud mame zastavit i pri nalezeni vymazaneho souboru */
        result = END_OF_DIR;
        break;
      }

      _dir_find_file->buffer += 32;                                   /* posuneme pointer o 1 jednotku */
      result++;                                                       /* pricteme navratovou hodnotu */
      lpos++;                                                         /* pricteme pozici ve strukture */
    } else {                                                          /* |- pokud jsme narazili na soubor nebo slozku */
      fat_associate_entry(_dir_find_file->buffer, sfile);             /* asociujeme promennou <sfile> s pameti */
      result++;                                                       /* pricteme navratovou hodnotu */
      lpos++;                                                         /* pricteme pozici ve strukture */
      break;
    }
  }

  _end_of_dir:

  /* vratime puvodni hodnoty */
  _dir_actual->c_actual = _dir_actual->c_start;
  _dir_actual->pos      = ((_fat->data_start_sector + (_dir_actual->c_actual - 2)));
  *_page  = page;
  *_pos   = lpos;

  return result;
}

/**
 *  find_file()
 *  najde soubor v aktualni slozce na pozici <pos>, nastavi <sfile>
 *  IN:   <sfile>         - [DirectoryEntry], tato hodnota se asociuje
 *        <pos>           - pozice ve slozce
 *  OUT:  [int] pristi pozice
 */
int find_file(DirectoryEntry far *sfile, int pos) {
  int _page = 0, _pos = 0;
  return find_file_care(sfile, pos, &_page, &_pos, false);
}

/**
 *  goto_dir()
 *  z aktualni slozky se presune do jine
 *  IN:   <nextdir>       - nazev slozky
 *  OUT:  [int] vysledek  - 0 > zmena byla uspesna      {CHDIR_OK}
 *                          1 > zaznam neni slozka      {CHDIR_NOT_DIR}
 *                          2 > slozka nebyla nalezena  {CHDIR_NOT_FOUND}
 */
int goto_dir(char far *nextdir) {
  int i                       = 0;                  /* promenna pro cyklus */
  int result                  = CHDIR_NOT_FOUND;    /* navratova hodnota, implicitne na "nenalezeno" */
  DirectoryEntry far *sfile   = mem_alloc(sizeof(DirectoryEntry),
                                  KERNEL_SEGMENT);  /* promenna pro strukturu souboru/slozky */
  char far *nextdirLowercase  = mem_alloc(strlen(nextdir)+1,
                                  KERNEL_SEGMENT);  /* do teto promenne se nakopiruje <nextdir>, ale malymi posmeny */
  char far *near_nextdir;                           /* promena v lokalnim segmentu, kvuli kopirovani retezce globalDirString */

  strtolower(nextdir, nextdirLowercase);            /* prevedeme retezec na mala pismena */
  nextdir = nextdirLowercase;                       /* predefinujeme promennou na tento retezec */

  near_nextdir = mem_alloc(strlen(nextdir)+1, KERNEL_SEGMENT);
  strcpy(near_nextdir, nextdir);

  while((i = find_file(sfile, i)) != END_OF_DIR) {  /* cyklus, dokud nenarazime na konec slozky */
    if(!strcmp(sfile->realname, nextdir)) {         /* narazili jsme na pozadovanou slozku? */
      if(sfile->attr == F_DIRECTORY) {              /* je to slozka? */
        if(!sfile->ClstrNo) {                       /* |- jde se do korenoveho adresare "/"? */
          _dir_actual->c_start  = DIR_ROOT;
          _dir_actual->c_actual = DIR_ROOT;         /* _fat->data_start_sector + (-12 - 2) + 1 = 20 */
          _dir_actual->pos      = 20;               /*  > zde zacina "/" */
        } else {                                    /* |- nejde se do "/" */
          _dir_actual->c_start  = sfile->ClstrNo;   /* nastavime pocatenci cluster slozky */
          _dir_actual->c_actual = sfile->ClstrNo;   /* nastavime aktualni cluster na pocatecni */
          _dir_actual->pos      =
            (_fat->data_start_sector +
              (sfile->ClstrNo - 2));                /* dosadime cislo sektoru */
        }
        /*  #> zmena globalDirString */
        if(strcmp(nextdir, ".")) {                  /* nemuzeme skocit do aktualni slozky, a pricist "." */
          if(!strcmp(nextdir, "..")) {              /* |- pokud se vracime o slozku zpet */
            if(strcmp(_dir_actual->string, "/")) {  /* pokud se chceme vratit zpet z rootu, ukoncime akci */
              _dir_actual->string[l_pos(
                _dir_actual->string, '/')] = '\0';  /* ukoncime string pred poslednim lomitkem, vytvorime tim skok do podrazene slozky */
              if(!strlen(_dir_actual->string))      /* pokud je globalDirString prazdny retezec */
                strcpy(_dir_actual->string, "/");   /* nakopirujeme tam "/" */
            }
          } else {                                  /* |- pokud jdeme do jine realne slozky */
            if(!strcmp(_dir_actual->string, "/")) { /*   |- pokud menime slozku absolutne */
              strcpy(_dir_actual->string, "/");
              strcat(_dir_actual->string,
                      near_nextdir);                /* pricteme k retezci "/" danou slozku */
            } else {                                /*   |- nemenime slozku absolutne */
              strcat(_dir_actual->string, "/");
              strcat(_dir_actual->string, near_nextdir);
            }
          }
        }
        /*  #> zmena globalDirString / */
        result = CHDIR_OK;
        break;
      }
      result = CHDIR_NOT_DIR;                       /* neni slozka */
      break;
    }
  }

  /* uvolnime buffery */
  mem_free(nextdirLowercase);
  mem_free(sfile);
  mem_free(near_nextdir);

  return result;
}

/**
 *  change_dir()
 *  zmeni pracovni slozku s vyparsovanim dlouhych cest ([/]neco/nekde/nekam)
 *  IN:   <path>  - cesta, do ktere se presuneme
 *  OUT:  [int] vysledek  - 0 > zmena byla uspesna      {CHDIR_OK}
 *                          1 > zaznam neni slozka      {CHDIR_NOT_DIR}
 *                          2 > slozka nebyla nalezena  {CHDIR_NOT_FOUND}
 */
int change_dir(const char far *path) {
char far *dir           = mem_alloc(96, KERNEL_SEGMENT);    /* nazev slozky pri parsovani */
char far *dirBackupPtr  = dir;                              /* zaloha pointeruna tento string */
int   i                 = 1;                                /* cyklus */
bool  break_now         = false;                            /* hodnota, kdyz je true, blizi se konec posloupnosti */
int   result            = CHDIR_OK;                         /* konecna navratova hodnota */

#define assign(var, val) ((var=val)==val)

  if(*path == '/') {                                        /* pokud je cesta absolutni */
    _dir_actual->c_start  = DIR_ROOT;                       /* nastavime promenne na korenovy adresar */
    _dir_actual->c_actual = DIR_ROOT;
    _dir_actual->pos      =  20;                            /* nastavime pointer na slozku "/" */
     strcpy(_dir_actual->string, "/");                      /* nastavi globalDirString na root path */
    path++;                                                 /* posuneme ukazatel o lomitko "/" */
  }

  while(*path && assign(i, 1)) {                            /* dokud neprojedeme celou cestou, na zacatku cyklu misi byt v <i> jednicka */
    while ((*dir++ = *path++) != '/') {                     /* parsujeme slozku */
      if(!(*(path-1))) {
        break_now = true;
        break;
      }
      i++;
    }
    *(dir-1)='\0';
    dir -= i;                                               /* vratime pointer */

    result = goto_dir(dir);                                 /* skocime do slozky */

    if(break_now)
      break;                                                /* abychom nezajeli tam, kam nemame :) */
  }

  /* uvolnime buffer a vratime se */
  mem_free(dirBackupPtr);
  #undef assign

  return result;
}

/**
 *  get_current_working_dir()
 *  zkopiruje do bufferu <dir> retezec, obsahujici cestu k aktualni slozce
 */
char far *get_current_working_dir(char far *dir) {
  strcpy(dir, _dir_actual->string);
  return dir;
}

/**
 *  findfirst()
 *  najde prvni soubor v zadane slozce
 */
int findfirst(const char far *path, struct ffblk far *ffblk, int attrib) {
  DirectoryEntry far *sfile = mem_alloc(sizeof(DirectoryEntry), KERNEL_SEGMENT);
  char far *cwd             = mem_alloc(64, KERNEL_SEGMENT);
  int result = -1;

  strcpy(cwd, _dir_actual->string);
  change_dir(path);

  ffblk->attr = attrib;
  ffblk->path = mem_alloc(64, KERNEL_SEGMENT);
  strcpy(ffblk->path, _dir_actual->string);

  ffblk->ff_reserved = 0;

  /* najdeme prvni zaznam odpovidajici nasim atributum */
  while((ffblk->ff_reserved = find_file(sfile, ffblk->ff_reserved)) != END_OF_DIR)
    if(sfile->attr & ffblk->attr)
      break;

  /* pokud neni konec slozky */
  if(ffblk->ff_reserved != END_OF_DIR) {
    if(sfile->attr & ffblk->attr) {
      ffblk->ff_attrib   = (unsigned char)(sfile->attr);
      /* FIXME */
      /*ffblk->ff_ftime    = (sfile->time);
      ffblk->ff_fdate    = (sfile->date);*/
      ffblk->ff_fsize    = (unsigned int)(sfile->filesize);
      strcpy(ffblk->ff_name, sfile->realname);
      result = 0;
    }
  } else {
    ffblk->ff_attrib   = (unsigned char)0;
    ffblk->ff_ftime    = (unsigned int) 0;
    ffblk->ff_fdate    = (unsigned int) 0;
    ffblk->ff_fsize    = (unsigned int) 0;
    strcpy(ffblk->ff_name, "");
    result = -1;
  }

  change_dir(cwd);
  mem_free(sfile);
  mem_free(cwd);
  return result;
}

/**
 *  findnext()
 *  najde dalsi soubor
 */
int findnext(struct ffblk far *ffblk) {
  DirectoryEntry far *sfile = mem_alloc(sizeof(DirectoryEntry), KERNEL_SEGMENT);
  char far *cwd             = mem_alloc(64, KERNEL_SEGMENT);
  int result = -1;

  strcpy(cwd, _dir_actual->string);
  change_dir(ffblk->path);

  /* najdeme prvni zaznam odpovidajici nasim atributum */
  while((ffblk->ff_reserved = find_file(sfile, ffblk->ff_reserved)) != END_OF_DIR)
    if(sfile->attr & ffblk->attr)
      break;

  /* pokud neni konec slozky */
  if(ffblk->ff_reserved != END_OF_DIR) {
    if(sfile->attr & ffblk->attr) {
      ffblk->ff_attrib   = (unsigned char)(sfile->attr);
      /* FIXME */
      /*ffblk->ff_ftime    = (sfile->time);
      ffblk->ff_fdate    = (sfile->date);*/
      ffblk->ff_fsize    = (unsigned int)(sfile->filesize);
      strcpy(ffblk->ff_name, sfile->realname);
      result = 0;
    }
  } else {
    ffblk->ff_attrib   = (unsigned char)0;
    ffblk->ff_ftime    = (unsigned int) 0;
    ffblk->ff_fdate    = (unsigned int) 0;
    ffblk->ff_fsize    = (unsigned int) 0;
    strcpy(ffblk->ff_name, "");
    result = -1;
  }

  change_dir(cwd);
  mem_free(sfile);
  mem_free(cwd);
  return result;
}

/**
 *  findclose()
 *  uzavre praci s hledanim
 */
void findclose(struct ffblk far *ffblk) {
  mem_free(ffblk->path);
  return;
}
